package com.ccp.dev.system.configuration;


import com.alibaba.fastjson.JSON;
import com.ccp.dev.core.basic.api.ISysUser;
import com.ccp.dev.core.basic.response.ResultData;
import com.ccp.dev.core.basic.response.ReturnCode;
import com.ccp.dev.system.model.SysRole;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;

/**
 * 许可过滤器
 * @author  y
 */

public class CustomPermissionFilter extends AbstractSecurityInterceptor implements Filter {

	private FilterInvocationSecurityMetadataSource securityMetadataSource;

	private HashSet<String> staticResource;

	public void setStaticResource(HashSet staticResource) {
		this.staticResource = staticResource;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpServletRequest = (HttpServletRequest)request;
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		boolean flag = true;
		for(String pattern : staticResource){
			RequestMatcher requestMatcher = new AntPathRequestMatcher(pattern);
			if(requestMatcher.matches(httpServletRequest)){
				flag = false;
				break;
			}
		}
		if(flag){
			boolean canInvokeNext = canInvokeNextFilter(request, response, fi);
			if(canInvokeNext){
				invoke(fi);
			}
		}else{
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		}
	}

/**
	 * 判断是否是需要执行下一个过滤器<br>
	 * 目前在 非匿名 的 ajax请求 或 带有 isAjaxRequest=true 参数的情况下，需要判断session是否失效，
	 * 如果失效，则返回超时提示而不是登录页面的html代码
	 * @param request 	ServletRequest
	 * @param response 	ServletResponse
	 * @param fi 		FilterInvocation
	 * @return 			是否继续执行下一个过滤器
	 */

	private boolean canInvokeNextFilter(ServletRequest request, ServletResponse response, FilterInvocation fi) throws IOException{
		HttpServletRequest httpRequest = (HttpServletRequest)request;
		HttpServletResponse httpServletResponse = (HttpServletResponse)response;
		Collection<ConfigAttribute> configAttributes = this.obtainSecurityMetadataSource().getAttributes(fi);
		// 处理 非匿名 的 ajax请求 或 带有 isAjaxRequest=true 的请求，若session失效，则返回超时提示而不是抛出异常
		// 解决在session失效时，用ajax请求，返回的结果直接是登录页面
		if(!configAttributes.contains(SysRole.ROLE_CONFIG_ANONYMOUS)
				&& ("XMLHttpRequest".equalsIgnoreCase(httpRequest.getHeader("X-Requested-With"))
				|| "true".equalsIgnoreCase(httpRequest.getParameter("isAjaxRequest")))){
			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
			if (authentication == null || !(authentication.getPrincipal() instanceof ISysUser)) {
				httpServletResponse.setContentType("application/json; charset=utf-8");
                httpServletResponse.setStatus(1001);
				httpServletResponse.getWriter().print(JSON.toJSON(new ResultData(ReturnCode.TIMEOUT.getCode(),ReturnCode.TIMEOUT.getMsg(),0,null)));
				return false;
			}
		}
		return true;
	}

	public void invoke(FilterInvocation fi) throws IOException, ServletException {
		super.setRejectPublicInvocations(false);
		InterceptorStatusToken token = super.beforeInvocation(fi);
		try {
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		} finally {
			super.afterInvocation(token, (Object)null);
		}

	}

	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

	@Override
	public Class<? extends Object> getSecureObjectClass() {
		return FilterInvocation.class;
	}

	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

	public void setSecurityMetadataSource(
			FilterInvocationSecurityMetadataSource newSource) {
		this.securityMetadataSource = newSource;
	}
	@Override
	public void destroy() {
	}
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}
}
